package spring.cloud.tasks.executor.listener.report;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import spring.cloud.tasks.executor.common.Service;
import spring.cloud.tasks.executor.task.ExecuteNode;
import spring.cloud.tasks.executor.task.TaskExecutor;
import spring.cloud.tasks.executor.utils.log.LogEvents;
import spring.cloud.tasks.executor.utils.log.LogUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

@Slf4j
@Setter
@Getter
public class ReportService extends Service {
    public Map<Integer, TaskItemExecuteInfo> taskItemIndexToTaskItemExecuteInfoMap = new HashMap<>();

    public ReportService(TaskExecutor taskExecutor) {
        super(taskExecutor);
    }

    public void reportData2Zk() {
        synchronized (taskItemIndexToTaskItemExecuteInfoMap) {
            if (taskItemIndexToTaskItemExecuteInfoMap.size() != 0) {
                List<Integer> taskItemIndexList = taskExecutor.getExecuteContextService().getTaskItemIndexList();
                Iterator<Entry<Integer, TaskItemExecuteInfo>> iterator = taskItemIndexToTaskItemExecuteInfoMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<Integer, TaskItemExecuteInfo> next = iterator.next();
                    Integer taskItemIndex = next.getKey();
                    TaskItemExecuteInfo taskItemExecuteInfo = next.getValue();
                    if (!taskItemIndexList.contains(taskItemIndex)) {
                        LogUtils.info(log, LogEvents.ExecutorEvent.COMMON, "sharding items don't have such item: {}, reporter is going to ignore this " + "executionInfo: {}", taskItemIndex, taskItemExecuteInfo);
                        continue;
                    }
                    if (taskItemExecuteInfo.getLastBeginTime() != null) {
                        taskExecutor.getTaskNodeStorage().replaceTaskNode(ExecuteNode.getLastBeginTimeNode(taskItemIndex), taskItemExecuteInfo.getLastBeginTime());
                    }
                    if (taskItemExecuteInfo.getLastCompleteTime() != null) {
                        taskExecutor.getTaskNodeStorage().replaceTaskNode(ExecuteNode.getLastCompleteTimeNode(taskItemIndex), taskItemExecuteInfo.getLastCompleteTime());
                    }
                    if (taskItemExecuteInfo.getNextFireTime() != null) {
                        taskExecutor.getTaskNodeStorage().replaceTaskNode(ExecuteNode.getNextFireTimeNode(taskItemIndex), taskItemExecuteInfo.getNextFireTime());
                    }
                    taskExecutor.getTaskNodeStorage().replaceTaskNode(ExecuteNode.getLog(taskItemIndex), (taskItemExecuteInfo.getTaskItemLog() == null ? "" : taskItemExecuteInfo.getTaskItemLog()));
                    taskExecutor.getTaskNodeStorage().replaceTaskNode(ExecuteNode.getMessage(taskItemIndex), (taskItemExecuteInfo.getMessage() == null ? "" : taskItemExecuteInfo.getMessage()));
                    LogUtils.info(log, LogEvents.ExecutorEvent.COMMON, "done flushed {} to zk.", taskItemExecuteInfo);
                }
                taskItemIndexToTaskItemExecuteInfoMap.clear();
            }
        }
    }

    public void clearInfoMap() {
        synchronized (taskItemIndexToTaskItemExecuteInfoMap) {
            taskItemIndexToTaskItemExecuteInfoMap.clear();
        }
    }

    public void initTaskItemExecuteInfo(int taskItemIndex, Long nextFireTime) {
        synchronized (taskItemIndexToTaskItemExecuteInfoMap) {
            TaskItemExecuteInfo taskItemExecuteInfo = new TaskItemExecuteInfo(taskItemIndex, System.currentTimeMillis());
            if (nextFireTime != null) {
                taskItemExecuteInfo.setNextFireTime(nextFireTime);
            }
            taskItemIndexToTaskItemExecuteInfoMap.put(taskItemIndex, taskItemExecuteInfo);
        }
    }

    public void putTaskItemExecuteInfo(TaskItemExecuteInfo taskItemExecuteInfo) {
        synchronized (taskItemIndexToTaskItemExecuteInfoMap) {
            taskItemIndexToTaskItemExecuteInfoMap.put(taskItemExecuteInfo.getTaskItemIndex(), taskItemExecuteInfo);
        }
    }

    public TaskItemExecuteInfo getTaskItemExecuteInfo(int taskItemIndex) {
        synchronized (taskItemIndexToTaskItemExecuteInfoMap) {
            return taskItemIndexToTaskItemExecuteInfoMap.get(taskItemIndex);
        }
    }
}
